#include <GL/glew.h>
#include <GL/freeglut.h>

#include <iostream>
#include <fstream>
#include <thread>
#include <mutex>
#include <string>
#include <vector>
using namespace std;

#include "ShadowMap.h"
#include "Mesh.h"
#include "ogldev_camera.h"

class ShadowmapApp : public ICallbacks
{
public:
	ShadowmapApp()
		:m_camera(WIN_WIDTH, WIN_HEIGHT, Vector3f(0, 20, 0), Vector3f(0,-1,0), Vector3f(0,0,1)),
		m_obliquityOfEcliptic(23.43)
	{}

	bool Init(){
		if (!m_simpleTech.Init()) return false;
		if (!m_shadowmapTech.Init()) return false;
		if (!m_shadowmapFBO.Init()) return false;
		if (!m_buildings.LoadMesh("../../resource/box.obj")) return false;
		if (!m_quad.LoadMesh("../../resource/quad.obj")) return false;

		m_lightDir = Vector3f(1, 0.5, 0.6);
		m_lightDir.Normalize();

		m_persInfo.FOV = 60;
		m_persInfo.zFar = 1000;
		m_persInfo.zNear = 1;
		m_persInfo.Height = WIN_HEIGHT;
		m_persInfo.Width = WIN_WIDTH;
	}
	
	void KeyboardCB(OGLDEV_KEY OgldevKey) {
		m_camera.OnKeyboard(OgldevKey);
		m_camera.GetPos().Print();
		cout << "\n";
		Pipeline p;
		p.WorldPos(Vector3f(0, 0, 0));
		p.SetPerspectiveProj(m_persInfo);
		p.SetCamera(m_camera);
		p.GetWVPTrans().Print();
		p.Scale(1, 1, 1);
		p.Rotate(Vector3f(0, 0, 0));
		cout << "world\n";
		p.GetWorldTrans().Print();
		cout << "view\n";
		p.GetViewTrans().Print();
		cout << "wvp\n";
		p.GetWVPTrans().Print();
		
	};

	void PassiveMouseCB(int x, int y) {};

	void RenderSceneCB() {
		//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		//RenderShadowmap();
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glViewport(0, 0, WIN_WIDTH, WIN_HEIGHT);
		RenderScene();
		auto ret = glGetError();
		if (ret != GL_NO_ERROR){
			cout << ret << "\n";
		}
		
		OgldevBackendSwapBuffers();
	};

	void MouseCB(OGLDEV_MOUSE Button, OGLDEV_KEY_STATE State, int x, int y) {
		m_camera.OnMouse(Button, State, x, y);
	};

	void MouseMotion(int x, int y){
		m_camera.OnMotion(x, y);
	};
private:
	void RenderShadowmap(){
		m_shadowmapFBO.BindForWriting();
		m_simpleTech.Enable();

		

	}
	void RenderScene(){
		//m_shadowmapFBO.BindForReading(GL_TEXTURE0);
		m_shadowmapTech.Enable();

		Pipeline p;
		p.WorldPos(Vector3f(0, 0, 0));
		p.Scale(1000, 1000, 1000);
		p.Rotate(Vector3f(-90, 0, 0));

		p.SetPerspectiveProj(m_persInfo);
		p.SetCamera(m_camera);
		m_shadowmapTech.SetLightDir(m_lightDir);
		m_shadowmapTech.SetWorld(p.GetWorldTrans());
		m_shadowmapTech.SetWVP(p.GetWVPTrans());
		m_quad.Render();
		p.Scale(10, 10, 10);
		p.Rotate(Vector3f(0, 0, 0));
		m_shadowmapTech.SetWorld(p.GetWorldTrans());
		m_shadowmapTech.SetWVP(p.GetWVPTrans());
		m_buildings.Render();
	}
private:
	SimpleColorTechnique m_simpleTech;
	ShadowmapWithDirectionalLightTechnique m_shadowmapTech;
	ShadowmapFBO m_shadowmapFBO;
	BasicMesh m_buildings;
	BasicMesh m_quad;
	Camera m_camera;
	Vector3f m_lightDir;
	PersProjInfo m_persInfo;
	const float m_obliquityOfEcliptic;
};

void init(){
	glClearColor(1, 1, 1, 1);
	glClearDepth(1.0);

}

void input(){

}

int main(int argc, char *argv[]){

	OgldevBackendInit(OGLDEV_BACKEND_TYPE_GLUT, argc, argv, true, false);
	OgldevBackendCreateWindow(WIN_WIDTH, WIN_HEIGHT, false, "simulater");
	GLenum ret = glewInit();
	if (ret != GLEW_NO_ERROR){
		cout << "glew init failed\n";
		system("pause");
		return 1;
	}

	init();
	ShadowmapApp theApp;
	if (!theApp.Init()){
		cout << "app init failed\n";
		system("pause");
		return 1;
	}

	thread t(input);
	OgldevBackendRun(&theApp);
	t.join();

	return 0;
}